package jed;

import javaEventing.*;
import javaEventing.EventManager;
import udpInvoker.main.lib.Invoker;
import udpInvoker.main.lib.TopicSubscriber;

import java.util.HashMap;
import java.util.Map;

/**
 * This class must be used to extend javaEventing, and adds the JED functionality, allowing javaEventing to be used without modifications.
 * Always do EventManager.setEventManagerExtension(new DistributedEventManagerExtension())   before using JED (only once per JVM)
 * Created by Espen Skjervold, FFI, 02.feb.2011
 */
public class DistributedEventManagerExtension implements javaEventing.EventManager.EventManagerExtension {

    public final static String distributedEventTopic = "javaEventingDistributedEvent";
    private static boolean isListeningForDistributedEvents;
    private static Map<Integer, Object> locallyOriginatedDistributedEvents = new HashMap<Integer, Object>();


    public void afterTriggerEvent(Object sender, EventManager.Event event, Object conditionalExpression) {
        distributeEvent((DistributedEvent)event, conditionalExpression);
    }

    private static void distributeEvent(DistributedEvent distributedEvent, Object condition) {
        Invoker invoker = new Invoker();
        DistributedEventAndConditionPair distributedEventAndConditionPair = new DistributedEventAndConditionPair();
        distributedEventAndConditionPair.distributedEvent=distributedEvent;
        distributedEventAndConditionPair.condition= condition;
        locallyOriginatedDistributedEvents.put(distributedEventAndConditionPair.id, null);
        invoker.publish(distributedEventTopic, distributedEventAndConditionPair);
    }

    public void afterRegisterEventListener(EventManager.GenericEventListener genericEventListener, EventManager.Event event, EventManager.Condition condition, Map<String, Map<Integer, EventManager.EventSubscription>> eventSubscriptionLists) {
         if (event instanceof DistributedEvent && !isListeningForDistributedEvents) {
            startListeningForDistributedEvents(eventSubscriptionLists);
        }
    }

     private static void startListeningForDistributedEvents(final Map<String, Map<Integer, EventManager.EventSubscription>> eventSubscriptionLists) {
            Invoker invoker = new Invoker();
            invoker.subscribe(new TopicSubscriber() {
                public void deliverMessage(String s, Object o) {
                    DistributedEventAndConditionPair distributedEventAndConditionPair = (DistributedEventAndConditionPair) o;
                    DistributedEvent distributedEvent = distributedEventAndConditionPair.distributedEvent;

                    //if message originated in this client, abort
                    if (locallyOriginatedDistributedEvents.containsKey(distributedEventAndConditionPair.id)) {
                        locallyOriginatedDistributedEvents.remove(distributedEventAndConditionPair.id);
                        return;
                    }

                    Map<Integer, EventManager.EventSubscription> subscriptionList = eventSubscriptionLists.get(distributedEvent.getClass().getName());
                    EventManager.notifySubscribers(null, distributedEvent, subscriptionList, distributedEventAndConditionPair.condition );
                }
            }, distributedEventTopic);

            isListeningForDistributedEvents=true;

        }
}
